package syM

import (
	"db2s/Er"
	"db2s/Meta"
	metaD "db2s/MetaInit"
	"db2s/arg"
	"db2s/global"
	"db2s/go-log/log"
	"db2s/outPut"
	"db2s/parDef"
	"db2s/pubM"
	"db2s/ref"
	mq "db2s/topic-mq"
	"fmt"
	"os"
	"reflect"
	"sort"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

func structBreakPointF(config arg.RulesS) bool {
	switch config.Struct.Breakpoint {
	case true:
		return false
	default:
		return true
	}
}
func objectBreakPointF(config arg.RulesS) bool {
	switch config.Object.Breakpoint {
	case true:
		return false
	default:
		return true
	}
}
func breakPointFuncMap() map[string]reflect.Value {
	return map[string]reflect.Value{
		"object": reflect.ValueOf(objectBreakPointF),
		"struct": reflect.ValueOf(structBreakPointF),
	}
}
func getBreakPointStatus(config arg.RulesS) bool {
	if funcName, ok := breakPointFuncMap()[config.CheckMode]; ok {
		args := []reflect.Value{
			reflect.ValueOf(config),
		}
		l := funcName.Call(args)
		return l[0].Interface().(bool)
	}
	return false
}

func InitGlobalParametersConfig(l *SchemaInitContextConfig) (result *GlobalParametersConfig) {
	var m = l.GtCheckConfig
	var tl = l.TableList
	return &GlobalParametersConfig{
		repair:      m.SecondaryL.RepairV,
		rules:       m.SecondaryL.RulesV,
		dSns:        m.SecondaryL.DSnsV,
		TableObject: m.SecondaryL.TableObject,
		TableList:   tl,
		structs:     m.SecondaryL.RulesV.Struct,
		result:      m.SecondaryL.ResultV,
	}
}
func InitCloseObject(m arg.ConfigParameter, result *GlobalParametersConfig) {
	//closeObject = quit.GetClose(map[string]any{
	//	"dsn": map[string]any{"name": m.SecondaryL.DSnsV.DestDBName,
	//		"drive": m.SecondaryL.DSnsV.DestDrive,
	//		"jdbc":  m.SecondaryL.DSnsV.DestJdbc,
	//	},
	//}, p.DDB, []*os.File{result.FilePoint.BreakPointFin, result.FilePoint.ActiveDataFin, result.FilePoint.TtyBarFin, result.FilePoint.ObjectResult})
	global.GetIoc().RegisterContext("closeObject", closeObject)
}
func newXlsObject(baseDir string, config GlobalParametersConfig) {
	xlsFileName := fmt.Sprintf("%v/%v", baseDir, config.result.XlsV.XlsFileName)
	if !strings.Contains(xlsFileName, "/") {
		xlsFileName = fmt.Sprintf("./%v", xlsFileName)
	}
	xlsFile = outPut.NewFileOperations(outPut.Xls{
		FileName:  fmt.Sprintf("./%v.xlsx", xlsFileName),
		SheetName: []string{"terminal", "switch", "result", "miss"},
		Mode:      config.rules.CheckMode,
		Enroll:    []any{outPut.WorkSheetTerminal{}, outPut.WorkSheetSwitch{}, outPut.WorkSheetResult{}, outPut.WorkSheetMiss{}},
		Safety:    config.result.XlsV.XlsSafetySwitch,
	})
}
func initTextData(baseDir string, config GlobalParametersConfig) (err error) {
	textFile = outPut.NewFileOperations(outPut.Tex{
		SheetName: []string{"terminal", "switch", "result", "miss"},
		Mode:      config.rules.CheckMode,
		Enroll:    []any{outPut.WorkSheetTerminal{Persistence: baseDir}, outPut.WorkSheetSwitch{Persistence: baseDir}, outPut.WorkSheetResult{Persistence: baseDir}, outPut.WorkSheetMiss{Persistence: baseDir}},
		Safety:    false,
	})
	if err = textFile.Head(); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("Failed to write the TEXT header title. error is %v", err))
		return
	}
	return
}
func initXlsData(baseDir string, config GlobalParametersConfig) (err error) {
	newXlsObject(baseDir, config)
	if err = xlsFile.Create(); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("Failed to create the XLS file. error is %v", err))
		return
	}
	if err = xlsFile.Head(); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("Failed to write the XLS header title. error is %v", err))
		return
	}
	return
}
func initResultData(baseDir string, config GlobalParametersConfig) (err error) {
	//初始化输出目录result
	if !config.result.GeneralResult {
		log.MainLog().Warn(fmt.Sprintf("If the xls generation parameter is set to false, this step will be skipped."))
		return
	}
	log.MainLog().Info("Start initializing the Result data")
	baseDir = fmt.Sprintf("%v/xls", baseDir)
	if err = dirInit(baseDir); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("The creation of the %v directory failed. error is %v", baseDir, err))
		return
	}
	if initXlsData(baseDir, config) != nil {
		return
	}
	if initTextData(baseDir, config) != nil {
		return
	}
	log.MainLog().Info(fmt.Sprintf("The result data initialization has been completed"))
	return
}
func initOtherResultFile(baseDir, name, mode string, config GlobalParametersConfig) (result ReadWriteFilePointS, err error) {
	log.MainLog().Info(fmt.Sprintf("Start initializing the breakPoint file"))
	if strings.EqualFold(strings.TrimSpace(config.repair.DataFix), "file") {
		if err = os.RemoveAll(global.DataFix); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("remove data fix file fail. error info is %v", err))
		}
	}
	//newTextBreakPoint = outPut.NewFileOperations(outPut.Tex{FileName: fmt.Sprintf("result/breakPoint/%v/%v/%v", name, mode, global.BreakPointFileName), Truncate: getBreakPointStatus(config), RSymbol: "\n"})
	//if err = newTextBreakPoint.Init(); err != nil {
	//	log.ErrorLog().Error(fmt.Sprintf("init bar break point file fail. error info is %s", err))
	//	return
	//}
	breakP = pubM.NewBreakObject(pubM.BreakPoint{
		FileName: fmt.Sprintf("result/breakPoint/%v/%v/%v", name, mode, global.BreakPointFileName),
		Truncate: getBreakPointStatus(config.rules),
	})
	if breakP.Read() != nil {
		return
	}
	log.MainLog().Info(fmt.Sprintf("Start initializing the TTY BAR file"))
	if result.TtyBarFin, err = os.OpenFile(fmt.Sprintf("%v/%v.result", baseDir, global.MonitorBarFinishFileName), os.O_TRUNC|os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0664); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("init bar monitor finish file fail. error info is %s", err))
		return
	}
	log.MainLog().Info(fmt.Sprintf("Start initializing the parameterRecord file"))
	if result.ParameterRecord, err = os.OpenFile(fmt.Sprintf("result/%v", global.ParameterRecord), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0664); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("init %v file fail. error info is %s", global.ParameterRecord, err))
		return
	}
	log.MainLog().Info(fmt.Sprintf("Start initializing the Terminal Output file"))
	newTextTPod = outPut.NewFileOperations(outPut.Tex{FileName: fmt.Sprintf("%v/%v.data", baseDir, global.MonitorBarFinishFileName)})
	if err = newTextTPod.Init(); err != nil {
		return
	}

	return
}

// NewSchedulePlanConfig 初始化全局共享变量
func NewSchedulePlanConfig(l *SchemaInitContextConfig) (result *GlobalParametersConfig, err error) {
	var baseDir = fmt.Sprintf("result/end/%v_%v", l.GtCheckConfig.SecondaryL.RulesV.CheckMode, time.Now().Format("2006-01-02T15-04-05"))
	var event = "[NewSchedulePlanConfig]"
	result = InitGlobalParametersConfig(l)
	if result.db, err = pubM.InitDSnsPool(l.GtCheckConfig); err != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("create db connection fail. error info is {%s}", err))
		return
	}
	if err = initResultData(baseDir, *result); err != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("init result datadir fail. error info is {%s}", err))
		return
	}
	if result.FilePoint, err = initOtherResultFile(baseDir, l.GtCheckConfig.ArgInput.MetaDataCmd.Name, l.GtCheckConfig.ArgInput.MetaDataCmd.Mode, *result); err != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("init other result datadir fail. error info is {%s}", err))
		return
	}
	if pubM.UpdateModifyParameterSync(result.db.Target, result.FilePoint.ParameterRecord) != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("update modify parameter fail. error info is {%s}", err))
		return
	}
	InitCloseObject(l.GtCheckConfig, result)
	global.GetIoc().RegisterContext("globalConfig", result)
	return
}
func initSchemaBean(config *SchemaInitContextConfig) (err error) {
	//注册输入配置
	global.GetIoc().RegisterContext("configParameter", config.GtCheckConfig)
	//初始化全局共享配置 	//注册执行计划配置 SchedulePlanConfig
	if GlobalPConfigs, err = NewSchedulePlanConfig(config); err != nil || GlobalPConfigs == nil {
		return fmt.Errorf("初始化 NewSchedulePlanConfig 执行计划配置失败 %v", err)
	}
	return
}

// NewSchemaInitConfig 创建执行计划配置参数
func NewSchemaInitConfig() (err error) {
	var sourceT, destT Er.TablesMetaInfoEr
	gobject := &GlobalISObject{}
	if sourceT, err = Er.MetaInfo(Er.TableInfoMeta{
		DBType: GlobalPConfigs.dSns.SrcDBName,
	}); err != nil {
		return
	}
	if destT, err = Er.MetaInfo(Er.TableInfoMeta{
		DBType: GlobalPConfigs.dSns.DestDBName,
	}); err != nil {
		return
	}
	gobject.TableMetaData = map[string]Er.TablesMetaInfoEr{
		"source": sourceT,
		"dest":   destT,
	}
	global.GetIoc().RegisterContext("isObject", gobject)
	GlobalISObjects = gobject
	return
}
func getStructStatic() global.StartPartConfigInputP {
	q := GlobalPConfigs.rules.Struct
	return global.StartPartConfigInputP{
		Scn:             q.Scn,
		SqlExecStopTime: time.Second * time.Duration(q.TimeOutNew.Segment),
		SqlMode:         q.SqlMode,
	}
}
func getObjectStatic() global.StartPartConfigInputP {
	q := GlobalPConfigs.rules.Object
	return global.StartPartConfigInputP{
		Scn:             q.Scn,
		SqlExecStopTime: time.Second * time.Duration(q.TimeOutNew.Segment),
		SqlMode:         q.SqlMode,
	}
}
func getStatic(mode string) (res global.StartPartConfigInputP) {
	var p = map[string]reflect.Value{
		"struct": reflect.ValueOf(getStructStatic),
		"object": reflect.ValueOf(getObjectStatic),
	}
	if Func, ok := p[mode]; ok {
		for k, v := range Func.Call(nil) {
			switch k {
			case 0:
				return v.Interface().(global.StartPartConfigInputP)
			}
		}
	}
	return
}
func tableObjectSeqUnique(config *SchemaInitContextConfig) (p []int) {
	var m = make(map[int]int)
	for _, v := range config.GtCheckConfig.SecondaryL.TableObject {
		if _, ok := m[v.GroupSeq]; !ok {
			m[v.GroupSeq]++
			p = append(p, v.GroupSeq)
		}
	}
	sort.Ints(p)
	return
}
func tableObjectSeqUniqueSort(config *SchemaInitContextConfig) (result []any) {
	for _, s := range tableObjectSeqUnique(config) {
		var r = make(chan any, global.ChannelDefault)
		for _, q := range config.GtCheckConfig.SecondaryL.TableObject {
			if s == q.GroupSeq {
				r <- q
			}
		}
		close(r)
		result = append(result, r)
	}
	return
}

func getTableObject(v1 *arg.InputTableAttributes) (err error) {
	var exist bool
	r := Meta.NewMeta([]string{GlobalPConfigs.dSns.SrcDBName}, "left", v1.TableAttributes.Role)
	r.Parameter1 = append(r.Parameter1, parDef.Parameter{Con: GlobalPConfigs.db.Source.GetDB("single"), Object: parDef.Object{Schema: v1.Schema, Table: v1.Table}})
	r.Parameter1 = append(r.Parameter1, parDef.Parameter{Con: GlobalPConfigs.db.Target.GetDB("single"), Object: parDef.Object{Schema: v1.MSchema, Table: v1.MTable}})
	if exist, err = r.GetObjectExistence(); err != nil || exist {
		return
	}
	atomic.SwapInt64(v1.TableStatus, -1)
	return
}
func objectExistPointQuit(event string, _ any) (r any) {
	ref.MyWaitDel("objectExist")
	//log.MainLog().Info(fmt.Sprintf("%v When the breakpoint resume function is enabled, table set %v will be skipped.", "getBreakPointTableList", continueTableList))
	return
}
func objectExistPointInit(event string, f any) (r any) {
	var err error
	if f == nil || len(f.([]any)) == 0 {
		return
	}
	_, chanParameter, _ := schemaMetaExecFuncParameterSplit(f.([]any))
	re := chanParameter.(*arg.InputTableAttributes)
	if err = getTableObject(re); err != nil {
		fmt.Println("---------err:", err)
	}
	meta.TableObjectFlow <- *re
	return
}
func ignorePointContinue(event string, f any) (r any) {
	event = "[ignorePointContinue]"
	if f == nil || len(f.([]any)) == 0 {
		return
	}
	_, chanParameter, _ := schemaMetaExecFuncParameterSplit(f.([]any))
	v := chanParameter.(*arg.InputTableAttributes)
	if v.Ignore {
		meta.IgnoreObjectSum++
		log.MainLog().Info(fmt.Sprintf("%v When the ignorePoint resume function is enabled, the current table %v will be skipped.", event, getTableName(v.Schema, v.Table)))
		return true
	}
	return false
}
func objectExistInit(m any) (objectRoleSend mq.MonitorMsgMq) {
	ref.MyWaitAdd("objectExist")
	var wg sync.WaitGroup
	return mq.NewMonitorMsgMq1().SetSendMsg(m.(chan any)).SetCurryLimit(mq.SendCurryLimit{
		Wg:       &wg,
		Switch:   true,
		CurrySum: make(chan struct{}, GlobalPConfigs.rules.Mtc),
	}).SetNormalQuitFunc(global.EFunc{
		FuncName: objectExistPointQuit}).SetAbnormalQuitFunc(global.EFunc{
		FuncName: objectExistPointQuit}).SetContinueFunc(global.EFunc{
		FuncName: ignorePointContinue}).SetExecFunc([]global.EFunc{
		{FuncName: objectExistPointInit}})
}

// 初始化上下文，该模块的全局配置，过滤表名，按照join、left、方式
func schemaContext(config *SchemaInitContextConfig) *CheckSchemaMetaData {
	var event = "[schemaContext]"
	lock.Lock()
	defer lock.Unlock()
	metaD.WriteLog = global.LogWrite.MainL
	meta = GetCheckSchemaMetaData()
	meta.ObjectSum = len(config.GtCheckConfig.SecondaryL.TableObject)
	if initSchemaBean(config) != nil {
		close(meta.TableObjectFlow)
		return nil
	}
	fmt.Println(fmt.Sprintf("-- [%v] %v report: begin init table object group -- ", time.Now().Format("2006-01-02 15:04:05"), arg.ToolName))
	log.MainLog().Info(fmt.Sprintf("%v Start checking whether the group table object exists", event))
	go func() {
		defer func() {
			close(meta.TableObjectFlow)
		}()
		if pubM.ObjectExist(pubM.ObjectFlow{
			Drive:     []string{GlobalPConfigs.dSns.SrcDBName, GlobalPConfigs.dSns.DestDBName},
			DriveJoin: "left",
			Source:    GlobalPConfigs.db.Source,
			Target:    GlobalPConfigs.db.Target,
			Mtc:       GlobalPConfigs.rules.Mtc,
			IgnoreSum: &meta.IgnoreObjectSum,
			Object:    config.GtCheckConfig.SecondaryL.TableObject,
			Flow:      meta.TableObjectFlow,
		}) != nil {
			log.MainLog().Error(fmt.Sprintf("%v Failed to initialize table object flow", event))
		}
	}()
	return meta
}
